﻿using AlarmClock.BLL;
using AlarmClock.UI.Controls;
using Model;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using static AlarmClock.BLL.Service;

namespace AlarmClock.UI
{
    /// <summary>
    /// 定义闹钟【增、删、改】的委托
    /// </summary>
    /// <param name="clock">闹钟信息</param>
    /// <param name="order">指令。例如：【增加闹钟、删除闹钟、修改闹钟】</param>
    public delegate void ChangeClockHandler(Clock clock, string order);

    public partial class MainForm : Form
    {

        #region 解决界面切换闪烁问题

        //防止界面闪烁
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams paras = base.CreateParams;
                paras.ExStyle |= 0x02000000;
                return paras;
            }
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x0014) // 禁掉清除背景消息
                return;
            if (MouseButtons == MouseButtons.Right)//不响应非客户区域的右键
                return;
            base.WndProc(ref m);
        }
        #endregion

        #region 闹钟服务字段

        /// <summary>
        /// 闹钟服务 BLL层
        /// </summary>
        private ClockBLL clockBLL = new ClockBLL();

        /// <summary>
        /// 编号分配【1-100】
        /// </summary>
        public HashSet<int> clocksNum = new HashSet<int>();

        /// <summary>
        /// 闹钟最大数量限制
        /// </summary>
        private int clockMaxNum => Convert.ToInt32(clockBLL.ReadAllClockConfig().clockMaxNum);

        /// <summary>
        /// 退出时最小化
        /// </summary>
        private bool isExitToMiniMode => Convert.ToBoolean(clockBLL.ReadAllClockConfig().isExitToMiniMode);

        private System.Windows.Forms.Timer timerForDateTime;
        private System.Windows.Forms.Timer timerForWeather;
        private FrmClockSetting frmClockSetting;

        #endregion

        #region 天气服务字段
        private string cityName { get; set; } = ConfigurationManager.AppSettings["DefaultCityName"];//配置查询天气的城市
        private string weather => BaiduWeatherWebApi.AllWeatherInfo?.result?.now?.text;//天气
        private string weatherSearchInterval => ConfigurationManager.AppSettings["WeatherSearchInterval"];//天气查询的频率/s
        #endregion


        public MainForm()
        {
            InitializeComponent();

            this.ShowInTaskbar = false; // 不在任务栏中显示
            this.WindowState = FormWindowState.Minimized; // 最小化窗口

            //var test1 = FreeWeatherWebApi.GetChinaWeatherIDInfo();
            //var test2 = FreeWeatherWebApi.GetWeather("沙市");
        }

        /// <summary>
        /// 初始化加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FrmClock_Load(object sender, EventArgs e)
        {
            //设置软件图标
            string imagePath = Application.StartupPath + @"\Resourse\Picture\闹钟.png";
            notifyIcon.Icon = this.Icon = Icon.FromHandle(((Bitmap)Bitmap.FromFile(imagePath)).GetHicon());

            Weather.Click += Weather_Click;
            Weather.MouseHover += (s1, e1) => { Weather.ForeColor = Color.RoyalBlue; };
            Weather.MouseLeave += (s1, e1) => { Weather.ForeColor = Color.Black; };            
            VersionTips.MouseHover += (s1, e1) => { VersionTips.ForeColor = Color.RoyalBlue; };
            VersionTips.MouseLeave += (s1, e1) => { VersionTips.ForeColor = Color.FromKnownColor(KnownColor.Control); };
            
            //用户关闭窗体：隐藏程序或者退出程序
            this.FormClosing += FrmClock_FormClosing;
            //计划设置
            this.btn_PlanSet.Click += btn_PlanSet_Click;
            //增加闹钟计划
            this.btn_AddPlan.Click += btn_AddPlan_Click;
            //调整窗体大小会影响闹钟列表尾部刷新出一大片空白。需要改动滚动条
            this.SizeChanged += FrmClock_SizeChanged;

            //刷新闹钟
            this?.BeginInvoke(new Action(() =>
            {
                UpdateClock();
                InitialAlarmClockUI();

                timerForDateTime = new System.Windows.Forms.Timer();//1秒钟刷新一次
                timerForDateTime.Interval = 1 * 1000;//绑定更新事件
                timerForDateTime.Tick += timerForDateTime_Tick;
                timerForDateTime.Start();

                DoEventForWeather(null, null);
                timerForWeather = new System.Windows.Forms.Timer();
                bool b_GetInterval = int.TryParse(weatherSearchInterval, out var interval);
                timerForWeather.Interval = (b_GetInterval ? interval : 20) * 1000;//开天气查询API 20秒查一次 
                timerForWeather.Tick += DoEventForWeather;
                timerForWeather.Start();
            }));

            #region notifyIcon托盘设置

            //工具-时间差计算选项
            MenuItem notifyTools = new MenuItem("工具");
            MenuItem notifyClockCalculator = new MenuItem("时间差计算");
            notifyClockCalculator.Click += (s1, e1) =>
            {
                FrmTools frmTools = new FrmTools();
                frmTools.ShowDialog();
            };
            notifyTools.MenuItems.Add(notifyClockCalculator);
            //帮助选项
            MenuItem notifyExitHelp = new MenuItem("帮助");
            notifyExitHelp.Click += (s1, e1) => { MessageBox.Show("欢迎使用该软件，祝您使用愉快！", "帮助", MessageBoxButtons.OK); };
            //关于选项  
            MenuItem notifyExitAbout = new MenuItem("关于");
            //退出菜单项  
            MenuItem notifyExit = new MenuItem("退出");
            notifyExit.Click += (s1, e1) => { System.Environment.Exit(0); };//退出程序
            //单击后台图标
            notifyIcon.MouseClick += (s1, e1) => { if (e1.Button == MouseButtons.Left) { this.Show(); this.WindowState = FormWindowState.Normal; } };
            notifyExitAbout.Click += (s1, e1) => { MessageBox.Show("欢迎使用该软件，祝您使用愉快！", "关于", MessageBoxButtons.OK); };
            ////关联托盘控件  
            MenuItem[] childen = new MenuItem[] { notifyTools, notifyExitHelp, notifyExitAbout, notifyExit };
            notifyIcon.ContextMenu = new ContextMenu(childen);

            #endregion

            #region 右键菜单工具

            var contextMenuStripClock = new ContextMenuStrip();
            contextMenuStripClock.Font = this.Font;
            var menuTools = new ToolStripMenuItem("工具"); //嵌套：工具-时间差计算
            var menuTimePick = new ToolStripMenuItem("时间差计算");
            menuTimePick.Click += (s1, e1) =>
            {
                FrmTools frmTools = new FrmTools();
                frmTools.ShowDialog();
            };
            menuTools.DropDownItems.Add(menuTimePick);
            contextMenuStripClock.Items.Add(menuTools);

            this.ContextMenuStrip = contextMenuStripClock;

            #endregion
        }

        /// <summary>
        /// 天气设置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Weather_Click(object sender, EventArgs e)
        {
            FrmWeather frmWeather = new FrmWeather(cityName);
            frmWeather.ShowDialog();
            cityName = frmWeather.cityName;
            //开天气查询API
            Thread.Sleep(200);
            DoEventForWeather(null, null);
        }

        /// <summary>
        /// 用户关闭窗体：隐藏程序或者退出程序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FrmClock_FormClosing(object sender, FormClosingEventArgs e)
        {
            //主界面即将关闭，其他子窗体也都隐藏
            foreach (Form f in Application.OpenForms)
            {
                if (f == this) continue;
                f.Hide();
            }

            if (isExitToMiniMode)
            {
                e.Cancel = true;
                this.Hide();
            }
            else
            {
                notifyIcon.Dispose();
                Environment.Exit(0); //退出程序
            }
        }

        /// <summary>
        /// 绑定更新事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void DoEventForWeather(object sender, EventArgs e)
        {
            try
            {
                Weather.Text = await BaiduWeatherWebApi.GetMainWeather(cityName);
                //天气GIF
                string gifPath = Service.GetWeatherGIFPath(weather);
                pictureBoxForWeather.ImageLocation = gifPath;
                pictureBoxForWeather.SizeMode = PictureBoxSizeMode.StretchImage;
                pictureBoxForWeather.Load();
            }
            catch (Exception ex)
            {
                Service.ExceptionTraceLog(ex);
            }
        }

        /// <summary>
        /// 计划设置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_PlanSet_Click(object sender, EventArgs e)
        {
            frmClockSetting?.Close();
            frmClockSetting?.Dispose();

            frmClockSetting = new FrmClockSetting(this);
            frmClockSetting.Show();
        }

        /// <summary>
        /// 增加闹钟计划
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_AddPlan_Click(object sender, EventArgs e)
        {
            int count = clockMaxNum;
            if (panelMain.Controls.Count >= count)
            {
                ThreadPool.QueueUserWorkItem(obj=>new FrmMessageBox
                (
                    this.Location, 
                    this.Width, 
                    $"闹钟总数已达到{count}个,请删除多余闹钟后重试。"
                    ).ShowDialog());
                return;
            }
            FrmClockUpdate AddClock = new FrmClockUpdate(null, Constant.CLOCK_ADD);
            AddClock.ChangeClock += new ChangeClockHandler(changeClock);
            AddClock.ShowDialog();
        }

        /// <summary>
        /// 时钟定时更新、监测最近【闹钟】，到时触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timerForDateTime_Tick(object sender, EventArgs e)
        {
            UpdateClock(); //刷新时钟界面

            var Config = clockBLL.ReadAllClockConfig();

            //图标冒泡：提示最近闹钟即将响动
            if (this.WindowState == FormWindowState.Minimized)
            {
                var dis = (DateTime.Parse(Config.recentTime) - DateTime.Now).TotalSeconds;
                if (dis >= 30 && dis < 31)
                {
                    string curThing = clockBLL.ReadAllClockInfo().FirstOrDefault(c => c.clockInfo.time == Config.recentTime).clockInfo.things;
                    //根据最新官方文档，提示的停留时间是系统设置的，一般是7秒，设置已经弃用
                    notifyIcon.ShowBalloonTip(7 * 1000, this.Text, $"闹钟在30秒后响...\r\n事项： {curThing}", ToolTipIcon.Info);
                    return;
                }
            }

            //监测最近【闹钟】到时触发
            if (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") == Config.recentTime)
            {
                foreach (UserControl ctrl in panelMain.Controls)
                {
                    Clock curClock = ctrl.Tag as Clock;
                    if (curClock.clockInfo.time == Config.recentTime)
                    {
                        FrmAlarmClock frmAlarmClock = new FrmAlarmClock(curClock, "触发闹钟");
                        frmAlarmClock.ChangeClock += new ChangeClockHandler(changeClock);
                        frmAlarmClock.Show();
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 调整窗体大小会影响闹钟列表尾部刷新出一大片空白。需要改动滚动条
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FrmClock_SizeChanged(object sender, EventArgs e)
        {
            panelMain.AutoScroll = false;
            panelMain.AutoScroll = true;

            if (this.WindowState == FormWindowState.Minimized)//最小化到托盘
            {
                this.Hide();
            }
        }


        /// <summary>
        /// 闹钟【增删改】都会触发的函数回调
        /// </summary>
        /// <param name="clock">闹钟信息</param>
        /// <param name="order">指令。例如：【增加闹钟、删除闹钟、修改闹钟】</param>
        private void changeClock(Clock clock, string order)
        {
            if (clock == null) return;

            switch (order)
            {
                //增加闹钟
                case Constant.CLOCK_ADD:
                    //1.查找最小可以编号，并更新有限编号记录
                    int min = clocksNum.Min();
                    clocksNum.Remove(min);
                    clock.ClockName = "clock" + min;

                    //2.修改闹钟文件
                    clockBLL.InsertClock(clock);
                    break;
                //删除闹钟
                case Constant.CLOCK_DELETE:
                    //1.界面移除闹钟
                    Clock_Ctrl ctrl = panelMain.Controls[clock.ClockName] as Clock_Ctrl;
                    panelMain.Controls.Remove(ctrl);

                    //2.文件删除闹钟
                    clockBLL.DeleteClock(clock);

                    //3.更新有限编号记录
                    string clockNum = ""; 
                    int i = clock.ClockName.Length - 1;
                    while (i >= 0 && char.IsDigit(clock.ClockName[i]))
                    {
                        clockNum = clock.ClockName[i--] + clockNum;
                    }
                    int num = int.Parse(clockNum);
                    clocksNum.Add(num);

                    //4.更新配置信息：闹钟数量
                    var allConfig = clockBLL.ReadAllClockConfig();
                    allConfig.clockCount = (int.Parse(allConfig.clockCount) - 1).ToString();
                    clockBLL.UpdateClockConfig(allConfig);
                    break;
                //更新闹钟、关闭闹钟、稍后提醒
                case Constant.CLOCK_UPDATE:
                case Constant.CLOCK_CLOSE:
                case Constant.CLOCK_LATERALARMING:
                    //更新指定闹钟名称的显示信息
                    ctrl = panelMain.Controls[clock.ClockName] as Clock_Ctrl;
                    //更新绑定信息
                    ctrl.Tag = clock;
                    //时间
                    string time = clock.clockInfo.time;
                    ctrl.Hour_Ten.Text = time.Substring(11, 1);
                    ctrl.Hour_S.Text = time.Substring(12, 1);
                    ctrl.Minute_Ten.Text = time.Substring(14, 1);
                    ctrl.Minute_S.Text = time.Substring(15, 1);
                    //重复+事件
                    ctrl.RepeatAndThings.Text = clock.clockInfo.repeat + " | " + clock.clockInfo.things;
                    //激活状态
                    ctrl.check.Checked = clock.clockInfo.isActive == "1";
                    if (!ctrl.check.Checked)
                    {
                        ctrl.label1.ForeColor =
                            ctrl.Hour_Ten.ForeColor = ctrl.Hour_S.ForeColor = ctrl.Minute_Ten.ForeColor = ctrl.Minute_S.ForeColor
                            = Color.FromKnownColor(KnownColor.AppWorkspace);
                        ctrl.BackColor = Color.FromKnownColor(KnownColor.Control);
                    }
                    else
                    {
                        ctrl.label1.ForeColor =
                            ctrl.Hour_Ten.ForeColor = ctrl.Hour_S.ForeColor =
                            ctrl.Minute_Ten.ForeColor = ctrl.Minute_S.ForeColor = Color.Black;
                        ctrl.BackColor = Color.White;
                    }

                    //更新闹钟信息
                    clockBLL.UpdateClock(clock);
                    break;
                default:
                    return;
            }

            //3.重新加载闹钟界面，里面包含了 最近时间更新。直接返回就行
            this?.BeginInvoke(new Action(() => { InitialAlarmClockUI(); }));

            //最近时间更新
            this.BackColor = Color.White;
            clockBLL.UpdateRecentTime(clockBLL.ReadAllClockInfo());

        }


        /// <summary>
        /// 刷新闹钟界面
        /// </summary>
        private void InitialAlarmClockUI()
        {
            //1.读取全局配置信息
            var AllConfig = clockBLL.ReadAllClockConfig();
            //1.1分配clock编号：[1-maxSum]    
            clocksNum = new HashSet<int>();
            for (int i = 1; i <= clockMaxNum; i++)
                clocksNum.Add(i);

            //2.读取全部闹钟数据
            var Clocks = clockBLL.ReadAllClockInfo();
            //2.1更新闹钟数量 、 在界面显示数量、 软件版本号
            AllConfig.clockCount = Clocks.Count.ToString();
            ClockSumTips.Text = $"闹钟总数：{Clocks.Count}";
            VersionTips.Text = $"版本：{Assembly.GetExecutingAssembly().GetName().Version}";
;            clockBLL.UpdateClockConfig(AllConfig);
            if (Clocks.Count == 0) return;//没有数据直接返回    

            //2.2排序
            Clocks.Sort((a, b) => a.clockInfo.time.CompareTo(b.clockInfo.time));//按时间排序，从大到小

            //3.1载入闹钟数据
            panelMain.Controls.Clear();

            for (int i = 0; i < Clocks.Count; i++)
            {
                string clockName = Clocks[i].ClockName;

                //更新有限编号记录
                string clockNum = ""; 
                int j = clockName.Length - 1;
                while (j >= 0 && char.IsDigit(clockName[j]))
                {
                    clockNum = clockName[j--] + clockNum;
                }
                int num = int.Parse(clockNum);
                clocksNum.Remove(num);

                //添加闹钟控件,绑定闹钟信息
                Clock_Ctrl curClock = new Clock_Ctrl(Clocks[i]);
                //注册回调函数，内部执行方法时，会触发回调更新UI
                curClock.ChangeClock += new ChangeClockHandler(changeClock);

                panelMain.Controls.Add(curClock);

            }
            //4.最近时间更新                                                                    
            clockBLL.UpdateRecentTime(Clocks);
        }


        /// <summary>
        /// 刷新时钟界面
        /// </summary>
        private void UpdateClock()
        {
            this?.Invoke(new Action(() => {
                DateTime now = DateTime.Now;

                dateAndWeek.Text = now.ToString("yyyy年MM月dd日 dddd");//日期

                Hour_Ten.Text = (now.Hour / 10).ToString();//时钟
                Hour_S.Text = (now.Hour % 10).ToString();

                Minute_Ten.Text = (now.Minute / 10).ToString();//分钟
                Minute_S.Text = (now.Minute % 10).ToString();

                Second_Ten.Text = (now.Second / 10).ToString();//秒钟
                Second_S.Text = (now.Second % 10).ToString();

                //定时更新【标签】时间差
                var diff = clockBLL.GetMinDisTime();//距离最近【闹钟】响的时差

                string msg = "";
                if (diff == null)
                {
                    msg = "当前没有闹钟";
                }
                else
                {
                    msg = $"{diff.Day} 天 {diff.Hour} 小时 {diff.Minute} 分钟后闹钟响";
                    if (diff.Day >= 0 && diff.Hour >= 0)
                    {
                        if (diff.Minute == 0)
                        {
                            msg = "不到 1 分钟后闹钟响";
                        }
                    }
                }

                RecentDisTips.Text = msg;
                notifyIcon.Text = "天气：" + Weather.Text + "\r\n" + RecentDisTips.Text;
            }));

        }
    }
}



